<?php
/**
 * phptm
 *
 * Copyright (c) 2009, Olivier Hoareau <hoareau.olivier@gmail.com>.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in
 *     the documentation and/or other materials provided with the
 *     distribution.
 *
 *   * Neither the name of Olivier Hoareau nor the names of his
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * @package   phptm
 * @author    Olivier Hoareau <hoareau.olivier@gmail.com>
 * @copyright 2009 Olivier Hoareau <hoareau.olivier@gmail.com>
 * @license   http://www.opensource.org/licenses/bsd-license.php  BSD License
 * @since     File available since Release 1.0.0
 */

/**
 * PHPTM code analyzer.
 *
 * @author    Johann-Peter Hartmann <johann-peter.hartmann@mayflower.de>
 * @author    Sebastian Bergmann <sb@sebastian-bergmann.de>
 * @author    Olivier Hoareau <hoareau.olivier@gmail.com>
 * @copyright 2009 Olivier Hoareau <hoareau.olivier@gmail.com>
 * @license   http://www.opensource.org/licenses/bsd-license.php  BSD License
 * @version   Release: @package_version@
 * @link      http://code.google.com/p/phptreematch
 * @since     Class available since Release 0.1.0
 */
class PHPTM_Analyzer
{
    /**
     * Tree match analyzer (TM).
     *
     * @param  Iterator|array   $files     List of files to process
     * @param  array            $expected  List of expected
     * 
     * @author Olivier Hoareau <hoareau.olivier@gmail.com>
     * 
     * @return array
     */
    public static function analyze($dir,$filterfiles,$expected=null)
    {
        $results = array('errors'=>array());
        
        if (null === $expected || false === is_array($expected) || false === isset($expected['tree']) || false === is_array($expected['tree'])) {
            return $results;
        }
        
        $found = array();
        
        $files = array();
        foreach($filterfiles as $file) {
            $file = str_replace(DIRECTORY_SEPARATOR,'/',substr((string)$file,strlen($dir)+1));
            $files[$file] = true;
        }
        foreach($expected['tree'] as $treeItem) {
            $file = $treeItem['name'];
            if (true === isset($files[$file])) {
                if (true === isset($treeItem['hash'])) {
                    $hash = md5_file($dir.'/'.$file);
                    if ($treeItem['hash'] !== $hash) {
                        $results['errors'][] = array('file'=>$file,'type'=>'hash','expected'=>$treeItem['hash'],'found'=>$hash);
                    }
                }
                if (true === isset($treeItem['present']) && $treeItem['present'] === false) {
                    $results['errors'][] = array('file'=>$file,'type'=>'present');
                }
                if (true === isset($treeItem['contains'])) {
                    $content = file_get_contents($dir.'/'.$file);
                    foreach($treeItem['contains'] as $contains) {
                        if (false === strpos($content,trim($contains['content']))) {
                            $results['errors'][] = array('file'=>$file,'type'=>'not-contains','expected'=>$contains['name']);
                        }
                    }
                }
                if (true === isset($treeItem['notcontains'])) {
                    $content = file_get_contents($dir.'/'.$file);
                    foreach($treeItem['notcontains'] as $notcontains) {
                        if (false !== strpos($content,trim($notcontains['content']))) {
                            $results['errors'][] = array('file'=>$file,'type'=>'contains','expected'=>$notcontains['name']);
                        }
                    }
                }
                if (false === isset($found[$treeItem['name']])) {
                    $found[$treeItem['name']] = 0;
                }
                $found[$treeItem['name']]++;
            }
        }
        foreach($expected['tree'] as $treeItem) {
            if (false === isset($found[$treeItem['name']])) {
                $results['errors'][] = array('file'=>$treeItem['name'],'type'=>'missing'); 
            }
        }
        
        return $results;
    }
    
    public static function buildTreeFile($dir,$fh,$suffixes=array())
    {
        $files = new PHPTM_Util_FilterIterator(
            new RecursiveIteratorIterator(
            new RecursiveDirectoryIterator($dir)
            ),
            $suffixes
            );
        $tree = array();
        foreach($files as $file) {
            $rfile = str_replace(DIRECTORY_SEPARATOR,'/',substr($file,strlen($dir)+1));
            $info = array('name'=>$rfile);
            if (!is_dir($file)) {
                $info['hash'] = md5_file($file);
            }
            $tree[$rfile] = $info; 
        }
        $document = new DOMDocument('1.0', 'UTF-8');
        $document->formatOutput = TRUE;

        $t = $document->createElement('tree');
        foreach($tree as $file=>$data) {
            $i = $document->createElement('item');
            foreach($data as $k=>$v) {
                $i->setAttribute($k,$v);
            }
            $t->appendChild($i);
        }
        $document->appendChild($t);
        fputs($fh,$document->saveXML());
    }
    public static function parseTreeFile($file)
    {
        if (false === file_exists($file)) {
            throw new RuntimeException("File '$file' does not exist");
        }
        $x = simplexml_load_file($file);
        
        $tree = self::parseTree($x);
        
        return $tree;
    }
    
    public static function parseTree(SimpleXMLElement $x,$root=null)
    {
        $tree = array();
        $root = (null === $root ? '' : $root.'/').(string)$x['name'];
        if ($root) {
            $tree[] = array('name'=>$root);
        }
        foreach($x->item as $item) {
            $info = array('name'=>$root.($root ? '/' : '').(string)$item['name'],'contains'=>array(),'denied'=>false);
            if ((string)$item['hash']) {
                $info['hash'] = (string)$item['hash'];
            }
            if ((string)$item['present']) {
                $info['present'] = (string)$item['present'] === 'false' ? false : true;
            }
            if (count($item->contains)>0) {
                foreach($item->contains as $contains) {
                    $info['contains'][] = array('name'=>(string)$contains['name'],'content'=>(string)$contains);
                }
            }
            if (count($item->notcontains)>0) {
                foreach($item->notcontains as $notcontains) {
                    $info['notcontains'][] = array('name'=>(string)$notcontains['name'],'content'=>(string)$notcontains);
                }
            }
            $tree[] = $info;
        }
        return array('tree'=>$tree);
    }
}
